package de.hsb.ai.devil.agent;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Result;
import jade.core.Agent;
import jade.domain.FIPAAgentManagement.FailureException;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.domain.introspection.EventRecord;
import jade.domain.introspection.Occurred;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.SimpleAchieveREResponder;

import org.apache.log4j.Logger;

import de.hsb.ai.devil.domain.action.GetDelta;
import de.hsb.ai.devil.domain.event.GameOver;
import de.hsb.ai.devil.util.DevilHelper;

/**
 * AbstractDevilPlayer provides common player logic.
 * All devil players should inherit from this class.
 */
@SuppressWarnings("serial")
public abstract class AbstractDevilPlayer extends AbstractDevilAgent {
	
	/**
	 * The abstract service-type.
	 */
	public static final String SERVICE_TYPE = "devil-player";
	
	public static final Logger logger = Logger.getLogger(SERVICE_TYPE);
	
	/**
	 * Creates a new {@link AbstractDevilPlayer}.
	 * @param serviceNamePrefix prefix to the service name
	 * @see #SERVICE_TYPE
	 */
	public AbstractDevilPlayer(String serviceNamePrefix) {
		super(serviceNamePrefix + '-' + SERVICE_TYPE, SERVICE_TYPE);
	}
	
	/**
	 * Called when a game is over.
	 * @param g the game-over-event
	 */
	public void handle(GameOver g) {
		logger.info("game over, I " + (g.getWinner().equals(getAID()) ? "win" : "lose"));
	}
	
	public abstract int getDelta(int total);
	
	@Override
	protected void setup() {
		super.setup();
		
		// add the default responder behavior
		addBehaviour(new Responder(this, 
				DevilHelper.createMessageTemplate(
						ACLMessage.REQUEST, ACLMessage.INFORM, ACLMessage.REFUSE)));
	}
	
	/**
	 * Handles an incoming action request.
	 * @param msg the incoming message
	 * @param action the action contained in the {@code msg}
	 * @return a response message (or {@code null})
	 * @throws NotUnderstoodException
	 * @throws RefuseException
	 */
	protected ACLMessage handleAction(ACLMessage msg, Concept action) throws NotUnderstoodException, RefuseException {
		ACLMessage res = msg.createReply();
		res.setPerformative(ACLMessage.NOT_UNDERSTOOD);
		
		if (action instanceof GetDelta) {
			res.setPerformative(ACLMessage.INFORM);
			Result result = new Result(action, getDelta(((GetDelta)action).getTotal()));
			logger.debug("taking " + result.getValue());
			try {
				getContentManager().fillContent(res, result);
			} catch (Exception e) {
				logger.error("creating action response", e);
			}
		} else {
			logger.warn("unexpected action");
			throw new NotUnderstoodException("unexpected action");
		}
		
		return res;
	}
	
	/**
	 * Handles an incoming event message.
	 * @param msg the incoming message
	 * @param event the event contained in {@code msg}
	 * @return a response message (or {@code null}
	 */
	protected ACLMessage handleEvent(ACLMessage msg, EventRecord event) {
		if (event.getWhat() instanceof GameOver) {
			handle((GameOver) event.getWhat());
		}	
		return null;
	}
	
	/**
	 * Player response-behavior.
	 */
	private class Responder extends SimpleAchieveREResponder {

		public Responder(Agent a, MessageTemplate mt) {
			super(a, mt);
		}
		
		@Override
		protected ACLMessage prepareResponse(ACLMessage request) throws NotUnderstoodException, RefuseException {
			try {
				ContentElement ce = getContentManager().extractContent(request);
				if (ce instanceof Action) {
					// handle action
					return handleAction(request, ((Action)ce).getAction());
				} else if (ce instanceof Occurred) {
					// handle event
					return handleEvent(request, ((Occurred)ce).getWhat());
				} else if (ce instanceof Result && request.getPerformative() == ACLMessage.REFUSE) {
					// Retry after refuse (not very elegant)
					// TODO implement some kind of retry-counter
					return handleAction(request, ((Result)ce).getAction());
				} else {
					logger.warn("unkown action/event");
					throw new NotUnderstoodException("unkonw action/event");
				}
			} catch (Exception e) {
				logger.error("unexpected request", e);
				throw new NotUnderstoodException("unexpected request");
			}
		}
		
		@Override
		protected ACLMessage prepareResultNotification(ACLMessage request,
				ACLMessage response) throws FailureException {
			return null;
		}
		
	}
	
	
}
